using System.Collections.Generic;
using System.Linq;
using BSDS.Core.Data.Local;
using BSDS.Core.Model.Services.Geocoding;
using BSDS.Core.Model.Validation;

namespace BSDS.Core.Model.Data
{
    public class BsdsData
    {
        public EntityValue[] Entities { get; private set; }
        public DataFormat OriginalFormat { get; private set; }
        public string OriginalFilename { get; private set; }
        public string EntityTypeName { get; set; }

        public BsdsData(EntityValue[] entities, string orignialFilename, DataFormat orignialFormat, string entityTypeName)
        {
            EntityTypeName = entityTypeName;
            Entities = entities;
            OriginalFilename = orignialFilename;
            OriginalFormat = orignialFormat;
        }

        public ValidationResult Validate()
        {
            return new BsdsDataValidator(this).Validate();
        }

        public void UpdateReverseGeocoding(EntityValue[] source)
        {
            var propertyNames = new[]{
                                         "AddressLine", "AdminDistrict", "CountryRegion",
                                         "District", "FormattedAddress", "Locality", "PostalCode",
                                         "PostalTown", "DisplayName", "EntityType"
                                     };

            UpdateProperties(source, propertyNames);
        }

        public void UpdateGeocoding(EntityValue[] source)
        {
            var propertyNames = new[] {
                "Latitude", 
                "Longitude", 
                "Confidence", 
                "MatchCode", 
                "MatchedMethod", 
                "EntityType", 
                "FormattedAddress",
                "Geocoder"
            };

            UpdateProperties(source, propertyNames);
        }

        /// <summary>
        /// Designed to detect lat/lon edits made through the Bing Maps Portal. 
        /// Requires MatchedMethod and Confidence columns in the dataset.
        /// </summary>
        public void FixMatchedMethod()
        {
            foreach (var entity in Entities)
            {
                if (!entity.AttributeInfos.Any(x => x.Name == "MatchedMethod"))
                    return;
                if (!entity.AttributeInfos.Any(x => x.Name == "Confidence"))
                    return;
                var mmValue = entity.GetAttributeValue("MatchedMethod");
                var confidenceValue = entity.GetAttributeValue("Confidence");

                if (confidenceValue == "BadRequest")
                    continue;
                
                if (mmValue.Trim() == string.Empty || mmValue.ToLower().Contains("manual") ||
                    confidenceValue == string.Empty)
                {
                    entity.UpdateAttributeValue("MatchedMethod", "Manual");
                }
            }
        }

        public EntityValue[] GetNotReadyEntities(GeocodingMode geocodingMode)
        {
            if (geocodingMode == GeocodingMode.Empty)
            {
                return Entities.Where(e => !e.HasValidCoordinates()).ToArray();
            }

            return new EntityValue[0];
        }

        public EntityValue[] GetImpreciseOrBadEntities()
        {
            return Entities.Where(e => e.IsImpreciseOrBadEntity()).ToArray();
        }

        public bool ContainsEntity(string EntityID)
        {
            return Entities.Any(e => e.PrimaryKeyValue == EntityID);
        }

        public EntityValue[] GetMatchingEntities(GeocodingResult selectedEntities)
        {
            return Entities.Where(e => selectedEntities.ContainsEntity(e.PrimaryKeyValue)).ToArray();
        }

        public bool GeocodingNeeded(GeocodingMode geocodingMode)
        {
            if (geocodingMode == GeocodingMode.Empty)
            {
                return Entities.Any(e => !e.HasValidCoordinates());
            }

            return true;
        }

        private void UpdateProperties(IEnumerable<EntityValue> source, string[] propertyNames)
        {
            //TODO: filter out unsuccessful results
            //geoData = FilterOutUnsuccessfulResults(geoData);

            var originalDataIndex = Entities.ToDictionary(e => e.PrimaryKeyValue, e => e); 
            var sourceDataIndex = source.ToDictionary(e => e.PrimaryKeyValue, e => e);

            foreach (var sourceKey in sourceDataIndex.Keys)
            {
                if (originalDataIndex.ContainsKey(sourceKey))
                {
                    var sourceDataEntity = sourceDataIndex[sourceKey];
                    var originalDataEntity = originalDataIndex[sourceKey];

                    foreach (var propertyName in propertyNames)
                    {
                        var p = originalDataEntity.AttributeValues.FirstOrDefault(a => a.AttributeInfo.Name == propertyName);
                        if (p == null)
                            continue;

                        originalDataEntity.UpdateAttributeFromOtherEntity(sourceDataEntity, propertyName);
                    }
                }
            }
        }

    }
}
